#ifndef ABSTRACT_LINE_GRAPHIC_ITEM_H
#define ABSTRACT_LINE_GRAPHIC_ITEM_H

#include <ellipsegraphicitem.h>
#include <iedgedata.h>
#include <ieditoritem.h>
#include <linegraphicitem.h>
#include <palette.h>
#include <rectgraphicitem.h>

#include <QtCore/QUuid>

namespace swp
{

class AbstractLineGraphicItem : public gui::LineGraphicItem,
    public core::IGraphicEditorItem
{
private:
    class TransformationMatrix : public gui::LineGraphicItem
    {
    public:
        TransformationMatrix(const QPainterPath &path,
            QGraphicsItem *parent = 0);
    };

    class ConnectionPoint : public gui::EllipseGraphicItem
    {
    public:
        ConnectionPoint(qreal x, qreal y, int w, int h,
            uint index, bool isAnchor, const QPainterPath& shape,
            QGraphicsItem* item, const QList<QPointF>& points,
            AbstractLineGraphicItem* parent = 0);

        int type() const;

    protected:
        virtual QVariant itemChange(GraphicsItemChange change,
            const QVariant &value);

        void mousePressEvent(QGraphicsSceneMouseEvent* event);
        void mouseReleaseEvent(QGraphicsSceneMouseEvent* event);

    private:
        uint m_index;
        bool m_isAnchor;
        QPainterPath m_shape;
        QGraphicsItem* m_item;
        QList<QPointF> m_points;
        AbstractLineGraphicItem* m_parent;
        QPointF m_lastPosition;
        friend class AbstractLineGraphicItem;
    };

    class Label : public gui::RectGraphicItem
    {
    public:
        Label(const QString& text, AbstractLineGraphicItem* parent = 0);

        QString text() const;
        void setText(const QString& text);

    private:
        void relayout();

    private:
        QGraphicsSimpleTextItem* m_textItem;
    };

public:
    enum LabelAnchor
    {
        StartAbove,
        StartBelow,
        MiddleAbove,
        MiddleBelow,
        EndAbove,
        EndBelow
    };

public:
    AbstractLineGraphicItem(const QPointF& startPoint,
        const QPointF& endPoint,
        const QList<QPointF>& anchors = QList<QPointF>(),
        QGraphicsItem* parent = 0);
    virtual ~AbstractLineGraphicItem();

    void setLabel(LabelAnchor anchor, const QString& label);
    QString label(LabelAnchor anchor) const;

    QPointF startPoint() const;
    QPointF endPoint() const;

    void setStartPoint(const QPointF& startPoint);
    void setEndPoint(const QPointF& endPoint);

    void addAnchorPoint(const QPointF& anchor);
    void setAnchorPoint(uint index, const QPointF& anchor);
    void removeAnchorPoint(const QPointF& anchor);
    void removeAnchorPoints();

    void setStartHandleSnapping(QGraphicsItem* item,
        const QList<QPointF>& connectPoints);
    void setEndHandleSnapping(QGraphicsItem* item,
        const QList<QPointF>& connectPoints);

    void setStartShape(const QPainterPath& shape);
    void setEndShape(const QPainterPath& shape);

    QList<QPointF> anchors() const;

    QPointF center(QPair<QPointF, QPointF>* centerPoints = 0) const;

    qreal startAngle(QPair<qreal, qreal>* vector = 0) const;
    qreal middleAngle(const QPair<QPointF, QPointF>& centerPoints,
        QPair<qreal, qreal>* vector = 0) const;
    qreal endAngle(QPair<qreal, qreal>* vector = 0) const;

    // IGraphicsEditorItem
    QPixmap thumbnail(core::ThumbnailRole role,
        int size = -1) const;

    void editorModeChange();

    void setContent(core::IGraphicItemContent* content);

    core::IGraphicItemContent* content() const;

    void setPalette(const core::Palette* const palette);

    virtual int paletteType() const;

    void scaleToFitContent();

    void handleContentRefresh();

    void handleConnectorPositionChange();

    void handleSelfConnect(core::IGraphicEditorItem* connector,
        const QPointF& cursorPos, bool previousSelfConnect = false);

    void ensureHandlesGeometryChanges(bool enabled);

    QPointF nearestConnectionPoint(const QPointF& to,
        core::ConnectionDirection direction) const;

    virtual void handleSceneResize();

    virtual void arrangeConnectorsRequest();

    virtual bool canBeDuplicated() const;

    virtual bool supportsConnection(
        const core::ConnectionData& connection) const;
    QList<core::ConnectionData> connections() const;
    void connectionsRefreshed();

    IEditorItem* parentEditorItem(core::IEditorItem* parent) const;
    void setParentEditorItem(core::IEditorItem* parent);

    core::IDocumentData* parentDocumentData() const;
    void setParentDocumentData(core::IDocumentData* data);

    virtual bool isModel() const;
    virtual bool isGraphic() const;
    virtual bool isText() const;

    QUuid id() const;

    // ISerializable
    bool doActionForProperty(const QString& property,
        const QVariantList& value,
        const core::SerializationEngine& helper);
    void deserializationFinished(bool ok);
    QHash<QString, QVariantList> serializationData(
        const core::SerializationEngine& helper) const;

    // INodeData
    void registerNode(core::Node* node);
    const core::Node* const node() const;

    // QGraphicsPathItem
    virtual int type() const;

protected:
    virtual QVariant itemChange(GraphicsItemChange change,
        const QVariant &value);

    void setStartArrow(QGraphicsPathItem* item);
    void setEndArrow(QGraphicsPathItem* item);

    virtual QPair<QPen, QBrush> startArrowPalette() const;
    virtual QPair<QPen, QBrush> endArrowPalette() const;

    void setLineStyle(Qt::PenStyle style);

    void internalCopy(AbstractLineGraphicItem* copy) const;

    void updateLine(bool updateHandles = true);

    void setHandlesMovable(bool movable = true);

private:
    void createBasicLookAndFeel();
    void createHighlight();
    void createTransformationMatrix();
    void deleteHighlight();
    void deleteTransformationMatrix();
    void updateHandles();
    void getLine(int strokeWidth, QPainterPath& path);
    QPointF arrowPos(const QRectF& itemBoudingRect,
        const QPointF& pos, qreal angle) const;
    QPointF labelPos(const QRectF& itemBoudingRect, LabelAnchor anchor,
        int startQuadrant, int middleQuadrant, int endQuadrant,
        const QPair<qreal, qreal>& startVector,
        const QPair<qreal, qreal>& middleVector,
        const QPair<qreal, qreal>& endVector,
        const QPointF& center) const;
    qreal angle(const QPair<QPointF, QPointF>& points) const;
    int quadrant(qreal angle) const;

    void setStartPointSilent(const QPointF& startPoint);
    void setEndPointSilent(const QPointF& endPoint);
    void setAnchorPointSilent(uint index, const QPointF& anchor);

    void notifyOfHandleChange(bool startHandle);

    void enableHandles();

    void refreshPalette();

protected:
    QUuid m_id;
    QPainterPath m_line;
    core::IGraphicItemContent* m_content;
    mutable QPixmap m_thumbnail;
    QPointF m_startPoint;
    QPointF m_endPoint;
    QList<QPointF> m_anchorsPoints;
    QGraphicsPathItem* m_selectionHighlight;
    TransformationMatrix* m_transformMatrix;
    QList<ConnectionPoint* > m_handles;
    QList<core::ConnectionData> m_connections;
    core::IEditorItem* m_parentItem;
    core::IDocumentData* m_documentData;
    QPainterPath m_startShape;
    QPainterPath m_endShape;
    QGraphicsItem* m_startItem;
    QGraphicsItem* m_endItem;
    QList<QPointF> m_startItemConnectPoints;
    QList<QPointF> m_endItemConnectPoints;
    const core::Palette* m_palette;
    gui::EllipseGraphicItem* m_centerPoint;
    QGraphicsPathItem* m_startArrow;
    QGraphicsPathItem* m_endArrow;
    QRectF m_startArrowBoundingRect;
    QRectF m_endArrowBoundingRect;
    QHash<LabelAnchor, Label*> m_labels;
    Qt::PenStyle m_lineStyle;
    core::Node* m_node;
    bool m_handlesMovable;

    friend class ConnectionPoint;
};

} // namespace swp

#endif
